﻿using UnityEngine;
using System.Collections;

public class Dungeon : MonoSingleton<Dungeon> 
{
	// Dungeon Rooms
	public int DUNGEON_SIZE_X = 5;
	public int DUNGEON_SIZE_Y = 5;
	
	// Size of 3D Model Prefab in World Space
	public int ROOM_SIZE_X = 13; 
	public int ROOM_SIZE_Z = 9;

	public int Seed = 0;
	[HideInInspector]
	public int seedGrain = 0;

	// Goal Items


	// Demo Room Prefab
	public GameObject RoomPrefab1;
	public GameObject RoomPrefab2;
	public GameObject RoomPrefab3;
	public GameObject RoomPrefab4;
	public GameObject RoomPrefab5;
	public GameObject RoomPrefab6;

	// Room structure
	public Room[,] rooms;
	
	// Pointer to Boss Room "Demo" GameObject
	private GameObject bossRoom;
	private int bossX, bossY;
	[HideInInspector]
	public GameObject ServerSpawn;
	[HideInInspector]
	public GameObject ClientSpawn;

	public Vector3 GoalItem1Spawn;
	public Vector3 GoalItem2Spawn;
	public Vector3 GoalItem3Spawn;

	public override void Init () 
	{
		DontDestroyOnLoad (transform.root.gameObject);
	}
	
	void Update () 
	{
	
	}

	private int CropSeed(int modulus) {
		int result = seedGrain % modulus;
		seedGrain /= modulus;

		if (0 == seedGrain) {
			Debug.Log ("Seed Wrap Event!");
			seedGrain = Seed;
		}

		return result;
	} // end method

	public void GenerateDungeon()
	{
		// Create room structure
		rooms = new Room[DUNGEON_SIZE_X,DUNGEON_SIZE_Y];

		if (0 == Seed)
			Seed = Random.Range (0, int.MaxValue);
		seedGrain = Seed;

		// Create our first room at a random position
		int roomX = bossX = CropSeed (DUNGEON_SIZE_X);
		int roomY = bossY = CropSeed (DUNGEON_SIZE_Y);
		int nextX, nextY;
		
		Room firstRoom = AddRoom(null, roomX,roomY); // null parent because it's the first node

		ArrayList roomStack = new ArrayList ();
		int direction = CropSeed(4);
		ArrayList directionStack = new ArrayList ();

		do {
			bool retreat = true;
			roomX = firstRoom.x;
			roomY = firstRoom.y;

			for (int i = direction; (i = (i + 1) % 4) != direction;) {
				switch (i) {
				case 0: /*LEFT*/
					if (0 == roomX)
						continue;

					nextX = roomX - 1;
					nextY = roomY;
					break;
				
				case 1: /*TOP*/
					nextY = roomY + 1;
					if (DUNGEON_SIZE_Y == nextY)
						continue;
					nextX = roomX;
					break;

				case 2: /*RIGHT*/
					nextX = roomX + 1;
					if (DUNGEON_SIZE_X == nextX)
						continue;
					nextY = roomY;
					break;

				case 3: /*BOTTOM*/
					if (0 == roomY)
						continue;

					nextX = roomX;
					nextY = roomY - 1;
					break;

				default: // Impossible condition.
					nextX = nextY = 0;
					break;
				} // end switch

				if (rooms[nextX, nextY] == null && 3 != firstRoom.NumChildren()) {
					Debug.Log("Room: " + roomX + "," + roomY + " dir: " + direction);

					roomStack.Add (firstRoom);
					firstRoom = AddRoom(firstRoom, nextX, nextY);
					directionStack.Add (direction);
					direction = (direction + 2) % 4;
					retreat = false;
					break;
				} // end if
			} // end for

			if (retreat) {
				int lastIndex = roomStack.Count - 1;
				if (-1 == lastIndex) {
					return;
				}

				firstRoom = (Room)roomStack[lastIndex];
				roomStack.RemoveAt(lastIndex);
				direction = (int)directionStack[lastIndex];
				directionStack.RemoveAt(lastIndex);
			} // end if: all neighbors have been explored
		} while (true);
	}
	
	void GenerateGameRooms()
	{
		int p2X = DUNGEON_SIZE_X - bossX - 1;
		int p2Y = DUNGEON_SIZE_Y - bossY - 1;
		GameObject[] prefabs = new GameObject[] {RoomPrefab1, RoomPrefab2, RoomPrefab3, RoomPrefab4, RoomPrefab5, RoomPrefab6};
		int prefabIndex = CropSeed (prefabs.Length);

		// For each room in our matrix generate a 3D Model from Prefab
		foreach (Room room in rooms)
		{
			if (room == null) continue;
			
			// Real world position
			float worldX = room.x * ROOM_SIZE_X;
			float worldZ = room.y * ROOM_SIZE_Z;
			
			GameObject g = GameObject.Instantiate(prefabs[prefabIndex], new Vector3(worldX,0,worldZ),Quaternion.identity) as GameObject;
			prefabIndex = (prefabIndex + 1) % prefabs.Length;
			
			// Add the room info to the GameObject main script (Demo)
			GameRoom gameRoom = g.GetComponent<GameRoom>();
			gameRoom.room = room;

			if (room.IsFirstNode()) 
			{
				bossRoom = g;
				g.name = "Boss Room";

				ServerSpawn = gameRoom.spawnPoint;
			}
			else {
				if (room.x == p2X && room.y == p2Y)
					ClientSpawn = gameRoom.spawnPoint;

				g.name = "Room " + room.x + " " + room.y;
			}
		}
	} // end method

	void GenerateItems()
	{
		int goalItemIndex1x = CropSeed(DUNGEON_SIZE_X);
		int goalItemIndex1y = CropSeed(DUNGEON_SIZE_Y);

		int goalItemIndex2x = CropSeed(DUNGEON_SIZE_X);
		int goalItemIndex2y = CropSeed(DUNGEON_SIZE_Y);

		int goalItemIndex3x = CropSeed(DUNGEON_SIZE_X);
		int goalItemIndex3y = CropSeed(DUNGEON_SIZE_Y);

		Room r = rooms[goalItemIndex1x, goalItemIndex1y];
		GoalItem1Spawn = new Vector3(r.x * ROOM_SIZE_X, 0.1f, r.y * ROOM_SIZE_Z);
		r = rooms[goalItemIndex2x, goalItemIndex2y];
		GoalItem2Spawn = new Vector3(r.x * ROOM_SIZE_X, 0.1f, r.y * ROOM_SIZE_Z);
		r = rooms[goalItemIndex3x, goalItemIndex3y];
		GoalItem3Spawn = new Vector3(r.x * ROOM_SIZE_X, 0.1f, r.y * ROOM_SIZE_Z);
	}

	// Helper Methods

	public void OnLevelWasLoaded (int level) {
		Debug.Log ("Dungeon.OnLevelWasLoaded: " + level);

		if (2 != level)
			return;

		GenerateDungeon();
		GenerateGameRooms();
		GenerateItems();

		ServerPrimary.instance.OnDungeonReady(this);

		// Camera looking at Boss Room for the demo
		Camera.main.transform.position = new Vector3(bossRoom.transform.position.x,10,bossRoom.transform.position.z - 10);
	} // end event
	
	public Room AddRoom(Room parent, int x, int y)
	{
		Room room = new Room(parent, x, y);
		rooms[x,y] = room;
		return room;
	}
}
